package club.kingyin.easycache.component.annotation.handler;

import club.kingyin.easycache.component.annotation.Context;
import club.kingyin.easycache.component.config.CacheConfiguration;
import club.kingyin.easycache.component.config.Config;
import club.kingyin.easycache.key.AbstractEasyCacheKey;
import club.kingyin.easycache.key.DefaultEasyCacheKey;
import club.kingyin.easycache.component.annotation.EasyCache;
import club.kingyin.easycache.component.annotation.MethodName;
import club.kingyin.easycache.component.annotation.Module;
import club.kingyin.easycache.key.EasyCacheKey;
import club.kingyin.easycache.method.CacheMethod;
import club.kingyin.easycache.proxy.ProxyKeyPostProcess;
import club.kingyin.easycache.utils.AnnotationUtils;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

import static club.kingyin.easycache.utils.AnnotationUtils.createByParams;

/**
 * 模块命名服务，确保缓存流程具备以模块隔离的功能
 * 该模块必须存在，否则Module注解和MethodName注解将无法生效
 */
@Slf4j
public class AnnotationModuleAutoSetPostProcess implements ProxyKeyPostProcess {

    @Override
    public void postProcessBeforeEasyCacheKey(CacheMethod target) {

    }

    // 解析Module注解和MethodName注解 EasyCache注解 EasyCache注解优先级最低
    @Override
    public EasyCacheKey postProcessAfterEasyCacheKey(CacheMethod target, EasyCacheKey key) {
        Method method = target.getMethod();
        // EasyCache 注解处理
        if (method.isAnnotationPresent(EasyCache.class)) {
            EasyCache easyCache = method.getDeclaredAnnotation(EasyCache.class);
            key.setModule(easyCache.module());
            if (!AnnotationUtils.PRAM_NULL.equals(easyCache.methodName()))
                key.setMethodName(easyCache.methodName());
            analysisCtx(easyCache.context(), key);
            if (easyCache.params().length > 0)
                key = createByParams(easyCache.params(), new DefaultEasyCacheKey.Builder().copyWith(key), key, null);
        }
        if (method.isAnnotationPresent(Module.class)) {
            Module module = method.getAnnotation(Module.class);
            key.setModule(module.value());
        }
        if (method.isAnnotationPresent(MethodName.class)) {
            MethodName methodName = method.getAnnotation(MethodName.class);
            key.setMethodName(methodName.value());
        }
        log.debug("模块：[{}] 方法：[{}] 构建EasyCacheKey {}", key.getModule(), key.getMethodName(), key);
        return key;
    }
    private void analysisCtx(Context[] cts, EasyCacheKey key) {
        try {
            // 局部大于全局
            for (Context ct : cts) {
                log.debug("模块：[{}] 方法：[{}] 加载局部CTX {}", key.getModule(), key.getMethodName(), ct);
                Object ctx = ct.type().getDeclaredMethod(ct.func()).invoke(null);
                ((AbstractEasyCacheKey) key).affiliated(ct.name(), ctx);
                log.debug("模块：[{}] 方法：[{}] 装载CTX：{}={}", key.getModule(), key.getMethodName(), ct.name(), ctx);
            }
            if (CacheConfiguration.contains(Config.CONTEXT) && !((AbstractEasyCacheKey) key)
                    .ancillary.containsKey(Config.CONTEXT)) {
                log.debug("模块：[{}] 方法：[{}] 加载全局CTX {}", key.getModule(), key.getMethodName(), CacheConfiguration
                        .get(Config.CONTEXT));
                Class<?> cls = (Class<?>) CacheConfiguration.get(Config.CONTEXT);
                Object ctx;
                ctx = cls.getDeclaredMethod((String) CacheConfiguration
                        .getOrDefault(Config.CONTEXT_NAME,Config.CONTEXT_GET)).invoke(null);
                ((AbstractEasyCacheKey) key).affiliated(Config.CONTEXT, ctx);
                log.debug("模块：[{}] 方法：[{}] 装载CTX：{}={}", key.getModule(), key.getMethodName(), Config.CONTEXT, ctx);
            }
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
}
